#ifndef AX_SEDIMENT_H
#define AX_SEDIMENT_H

#include <string>
#include <vector>
#include <utility>
#include <iostream>
#include "libAcoustX/export.h"

namespace ax {
namespace bhp {

static const int DECK41_FLOORTYPE_GRAVEL = 0; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_SAND = 1;  /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_SILT = 2; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_CLAY = 3; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_OOZE = 4; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_MUD = 5; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_ROCKS = 6; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_ORGANIC = 7; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_NODULES = 8; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_HARDBOTTOM = 9; /**< Value stored in NetCDF file */
static const int DECK41_FLOORTYPE_NODATA = 11; /**< Value stored in NetCDF file */

#define SEDIMENT_NOT_SET_VALUE (-10000.0)
typedef ::std::pair < int, int > Deck41Types;

/**
* \brief Surficial sediment geoacoustic parameters definitions
*
* The Sediment class provide an interface for creating and manipulating surficialg eoacoustic parameters.
*
**/
class AX_EXPORT Sediment {


public:

	/**
	* Sediment default constructor. The object created is not valid
	**/
	Sediment();

	/**
	* Sediment constructor
	* @param name textual name
	* @param velc compressional wave velocity [m/s]
	* @param vels shear wave velocity [m/s]
	* @param dens sediment density [g/cm^3 or user defined]
	* @param attc compressional wave attenuation [db/wavelength or user defined]
	* @param atts shear wave attenuation [db/wavelength or user defined]
	* @param bottom_depth bottom depth [m]
	**/
	Sediment(const ::std::string& name, double velc, double vels, double dens, double attc, double atts, double bottom_depth = 1.0);

	/**
	* Sediment copy constructor
	* @param copy Sediment to be copied
	**/
	Sediment(const Sediment& copy);


	/**
	* Sediment virtual factory method
	* @return a heap-created Sediment object
	**/
	virtual Sediment* create() const { return new Sediment(); }

	/**
	* Sediment virtual factory method
	* @param name textual name
	* @param velc compressional wave velocity [m/s]
	* @param vels shear wave velocity [m/s]
	* @param dens sediment density [g/cm^3 or user defined]
	* @param attc compressional wave attenuation [db/wavelength or user defined]
	* @param atts shear wave attenuation [db/wavelength or user defined]
	* @param bottom_depth bottom depth [m]
	* @return a heap-created Sediment object
	**/
	virtual Sediment* create(const ::std::string& name, double velc, double vels, double dens, double attc, double atts, double bottom_depth = 1.0) const {
		return new Sediment(name, velc, vels, dens, attc, atts, bottom_depth);
	}

	/**
	* Sediment virtual factory method
	* @param copy Sediment to be copied
	* @return a heap-created Sediment object
	**/
	virtual Sediment* create(const Sediment& copy) const { return new Sediment(copy); }

	/**
	* Sediment virtual factory method
	* @return a heap-created copy of <b>this</b> instance
	**/
	virtual Sediment* clone() const { return new Sediment(*this); }


	virtual ~Sediment() {};


	/**
	* Sets textual type name
	* @param name string name
	* @return reference to <b>*this</b>
	**/
	Sediment& setType(const ::std::string& name) { type = name; return *this; }

	/**
	* Sets compressional wave velocity
	* @param vel velocity [m/s]
	* @return reference to <b>*this</b>
	**/
	Sediment& setVelocityC(double vel) { vel_c = vel; return *this; }

	/**
	* Sets shear wave velocity
	* @param vel velocity [m/s]
	* @return reference to <b>*this</b>
	**/
	Sediment& setVelocityS(double vel) { vel_s = vel; return *this; }

	/**
	* Sets sediment density
	* @param dens density [g/cm^3 or user defined]
	* @return reference to <b>*this</b>
	**/
	Sediment& setDensity(double dens) { density = dens; return *this; }

	/**
	* Sets compressional wave attenuation
	* @param att attenuation [db/wavelength or user defined]
	* @return reference to <b>*this</b>
	**/
	Sediment& setAttenuationC(double att) { att_c = att; return *this; }

	/**
	* Sets shear wave attenuation
	* @param att attenuation [db/wavelength or user defined]
	* @return reference to <b>*this</b>
	**/
	Sediment& setAttenuationS(double att) { att_s = att; return *this; }

	/**
	* Sets bottom depth
	* @param bottom_depth positive depth [m/s]
	* @return reference to <b>*this</b>
	**/
	Sediment& setDepth(double bottom_depth) { depth = bottom_depth; return *this; }

	/**
	* Sets all parameters at one.
	* @param name textual name
	* @param velc compressional wave velocity [m/s]
	* @param vels shear wave velocity [m/s]
	* @param dens sediment density [g/cm^3 or user defined]
	* @param attc compressional wave attenuation [db/wavelength or user defined]
	* @param atts shear wave attenuation [db/wavelength or user defined]
	* @param bottom_depth bottom depth ( > 0 ) [m]
	* @return reference to <b>*this</b>
	**/
	Sediment& set(const ::std::string& name, double velc, double vels, double dens, double attc, double atts, double bottom_depth) {
		type = name; vel_c = velc; vel_s = vels, density = dens;
		att_c = attc; att_s = atts; depth = bottom_depth; return *this;
	}


	/**
	* Sets debug for the whole class
	* @param flag debug value
	**/
	static void setDebug(bool flag) { debug = flag; }


	/**
	* Gets compressional wave velocity
	* @return velocity [m/s]
	**/
	double getVelocityC() const { return vel_c; }

	/**
	* Gets shear wave velocity
	* @return velocity [m/s]
	**/
	double getVelocityS() const { return vel_s; }

	/**
	* Gets sediment density
	* @return density [g/cm^3 or user defined]
	**/
	double getDensity() const { return density; }

	/**
	* Gets compressional wave attenuation
	* @return attenuation [db/wavelength or user defined]
	**/
	double getAttenuationC() const { return att_c; }

	/**
	* Gets shear wave attenuation
	* @return attenuation [db/wavelength or user defined]
	**/
	double getAttenuationS() const { return att_s; }

	/**
	* Gets bottom depth
	* @return depth [m]
	**/
	double getDepth() const { return depth; }

	/**
	* Gets sediment type name
	* @return type
	**/
	::std::string getType() const { return type; }


	/**
	* Checks the validity of geoacoustic parameters provided
	* @return <i>true</i> if Sediment is valid, <i>false</i> otherwise
	**/
	virtual bool isValid() const {
		return (vel_c != SEDIMENT_NOT_SET_VALUE && vel_s != SEDIMENT_NOT_SET_VALUE && density != SEDIMENT_NOT_SET_VALUE
			&& att_c != SEDIMENT_NOT_SET_VALUE && att_s != SEDIMENT_NOT_SET_VALUE);
	}


	/**
	* Gets geoacoustic parameters in a formatted fashion
	* @return formatted string
	**/
	virtual const ::std::string getStringValues() const;


	/**
	* Assignment operator
	* @param copy const reference to a Sediment object to be copied
	* @return Sediment reference to <i>this</i>
	**/
	Sediment& operator=(const Sediment& time);


	/**
	* Sum operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator+(const Sediment& left, const Sediment& right);

	/**
	* Subtraction operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator-(const Sediment& left, const Sediment& right);

	/**
	* Division operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator/(const Sediment& left, const Sediment& right);

	/**
	* Multiplication operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator*(const Sediment& left, const Sediment& right);

	/**
	* Compound assignment sum operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator+=(Sediment& left, const Sediment& right);

	/**
	* Compound assignment subtraction operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator-=(Sediment& left, const Sediment& right);

	/**
	* Compound assignment division operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator/=(Sediment& left, const Sediment& right);

	/**
	* Compound assignment multiplication operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator*=(Sediment& left, const Sediment& right);


	/**
  * Compound assignment sum operator
  * @param left left operand reference
  * @param right right operand const reference
  * @return <i>left</i> reference after the operation
  **/
	friend AX_EXPORT Sediment& operator+=(Sediment& left, double right);

	/**
	* Compound assignment subtraction operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator-=(Sediment& left, double right);

	/**
	* Compound assignment division operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator/=(Sediment& left, double right);

	/**
	* Compound assignment multiplication operator
	* @param left left operand reference
	* @param right right operand const reference
	* @return <i>left</i> reference after the operation
	**/
	friend AX_EXPORT Sediment& operator*=(Sediment& left, double right);


	/**
	* Equality operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return true if <i>left == right</i>, false otherwise
	**/
	friend AX_EXPORT bool operator==(const Sediment& left, const Sediment& right);

	/**
	* Inequality operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return true if <i>left != right</i>, false otherwise
	**/
	friend AX_EXPORT bool operator!=(const Sediment& left, const Sediment& right);


	/**
	* Scalar sum operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator+(const double left, const Sediment& right);

	/**
	* Scalar subtraction operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator-(const double left, const Sediment& right);

	/**
	* Scalar division operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator/(const double left, const Sediment& right);

	/**
	* Scalar multiplication operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator*(const double left, const Sediment& right);


	/**
	* Scalar sum operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator+(const Sediment& left, double right);

	/**
	* Scalar subtraction operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator-(const Sediment& left, double right);

	/**
	* Scalar division operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator/(const Sediment& left, double right);

	/**
	* Scalar multiplication operator
	* @param left left operand const reference
	* @param right right operand const reference
	* @return a new const instance holding the operation result
	**/
	friend AX_EXPORT const Sediment operator*(const Sediment& left, double right);


	/**
	* << operator
	* @param os left operand ostream reference
	* @param instance right operand const Sediment reference
	* @return <i>os</i> reference after the operation
	**/
	friend AX_EXPORT::std::ostream& operator<<(::std::ostream& os, const Sediment& instance);


protected:

	/**
	* Sediment type name
	**/
	::std::string type;


	/**
	* Bottom depth. Used for shear wave velocity calculations [m]
	**/
	double depth;

	/**
	* Compressional wave velocity [m/s]
	**/
	double vel_c;

	/**
	* Shear wave velocity [m/s]
	**/
	double vel_s;

	/**
	* Sediment density [g/cm^3]
	**/
	double density;

	/**
	* Compressional wave attenuation [db/wavelength]
	**/
	double att_c;

	/**
	* Shear wave attenuation [db/wavelength]
	**/
	double att_s;


	/**
	* Debug flag
	**/
	static bool debug;


};

// non-inline friend AX_EXPORT  operator declarations
//////////
AX_EXPORT const Sediment operator/(const double left, const Sediment& right);

AX_EXPORT const Sediment operator*(const double left, const Sediment& right);

AX_EXPORT const Sediment operator/(const Sediment& left, const double right);

AX_EXPORT const Sediment operator*(const Sediment& left, const double right);


AX_EXPORT Sediment& operator+=(Sediment& left, const Sediment& right);

AX_EXPORT Sediment& operator-=(Sediment& left, const Sediment& right);

AX_EXPORT Sediment& operator/=(Sediment& left, const Sediment& right);

AX_EXPORT Sediment& operator*=(Sediment& left, const Sediment& right);


AX_EXPORT Sediment& operator+=(Sediment& left, double right);

AX_EXPORT Sediment& operator-=(Sediment& left, double right);

AX_EXPORT Sediment& operator/=(Sediment& left, double right);

AX_EXPORT Sediment& operator*=(Sediment& left, double right);

//inline functions
/////////////////
inline bool operator==(const Sediment& left, const Sediment& right) {
	if (&left == &right) return true;
	return(left.vel_c == right.vel_c && left.vel_s == right.vel_s && left.att_c == right.att_c && left.att_s == right.att_s
		&& left.density == right.density);
}


inline bool operator!=(const Sediment& left, const Sediment& right) {
	if (&left == &right) return false;
	return(left.vel_c != right.vel_c || left.vel_s != right.vel_s || left.att_c != right.att_c || left.att_s != right.att_s
		|| left.density != right.density);
}


inline ::std::ostream& operator<<(::std::ostream& os, const Sediment& instance) {
	os << "Sediment type = " << instance.type << "; velocity_c = " << instance.vel_c << "; velocity_s = " << instance.vel_s << "; density = " << instance.density
		<< "; attenuation_c = " << instance.att_c << "; attenuation_s = " << instance.att_s;
	return os;
}


inline const Sediment operator+(const Sediment& left, const Sediment& right) {
	return(Sediment(left.type + " + " + right.type, left.vel_c + right.vel_c, left.vel_s + right.vel_s, left.density + right.density, left.att_c + right.att_c, left.att_s + right.att_s));
}


inline const Sediment operator-(const Sediment& left, const Sediment& right) {
	return(Sediment(left.type + " - " + right.type, left.vel_c - right.vel_c, left.vel_s - right.vel_s, left.density - right.density, left.att_c - right.att_c, left.att_s - right.att_s));
}


inline const Sediment operator/(const Sediment& left, const Sediment& right) {
	return(Sediment(left.type + " / " + right.type, left.vel_c / right.vel_c, left.vel_s / right.vel_s, left.density / right.density, left.att_c / right.att_c, left.att_s / right.att_s));
}


inline const Sediment operator*(const Sediment& left, const Sediment& right) {
	return(Sediment(left.type + " * " + right.type, left.vel_c * right.vel_c, left.vel_s * right.vel_s, left.density * right.density, left.att_c * right.att_c, left.att_s * right.att_s));
}
///////////////  

/**
* \brief Gravel type implementation
*
* Literature gravel implementation
**/
class AX_EXPORT SedimentGravel : public Sediment {

public:

	SedimentGravel(double depth = 1.0);


protected:

	/**
	* Shear wave velocity formula taken from the literature
	**/
	double calculateVelocityS(double vels, double bottom_depth);

};


/**
* \brief Sand type implementation
*
* Literature sand implementation
**/
class AX_EXPORT SedimentSand : public Sediment {


public:


	SedimentSand();


};


/**
* \brief Silt type implementation
*
* Literature silt implementation
**/
class AX_EXPORT SedimentSilt : public Sediment {


public:


	SedimentSilt(double depth = 1.0);


protected:

	/**
	* Shear wave velocity formula taken from the literature
	**/
	double calculateVelocityS(double vels, double bottom_depth);


};


/**
* \brief Clay type implementation
*
* Literature clay implementation
**/
class AX_EXPORT SedimentClay : public Sediment {


public:


	SedimentClay();


};


/**
* \brief Ooze type implementation
*
* Literature ooze implementation
**/
class AX_EXPORT SedimentOoze : public Sediment {


public:


	SedimentOoze();


};


/**
* \brief Mud type implementation
*
* Literature mud implementation
**/
class AX_EXPORT SedimentMud : public Sediment {


public:


	SedimentMud(double depth = 1.0);


protected:

	/**
	* Shear wave velocity formula taken from the literature
	**/
	double calculateVelocityS(double vels, double bottom_depth);


};


/**
* \brief Rocks type implementation
*
* Literature rocks implementation
*
* @bug Needs a better geoacoustic representation
**/
class AX_EXPORT SedimentRocks : public Sediment {


public:


	SedimentRocks();


};


/**
* \brief Organic type implementation
*
* Literature organic implementation
*
* @bug No geoacoustic parameters avalaible
**/
class AX_EXPORT SedimentOrganic : public Sediment {


public:


	SedimentOrganic();


};


/**
* \brief Deck41 nodules type implementation
*
* Literature Deck41 nodules implementation
*
* @bug Needs a better geoacoustic representation
**/
class AX_EXPORT SedimentNodules : public Sediment {


public:


	SedimentNodules();


};


/**
* \brief Hard bottom type implementation
*
* Literature hard bottom implementation
**/
class AX_EXPORT SedimentHardBottom : public Sediment {


public:


	SedimentHardBottom();


};
}
}

#endif // !AX_SEDIMENT_H
